﻿using UnityEngine;
using System.Collections.Generic;
using System.Collections;
using System;
using UnityEngine.Networking;
using System.Diagnostics;
using System.Linq;

/// <summary>
/// 资源管理，加载和卸载，路径分为Resources（prefab）和Application.persistentDataPath（assetbundle）
/// </summary>
public class ResManager : MonoBehaviour
{
    public static ResManager Instance { get; private set; }

    public static Dictionary<string, AssetBundle> abs = new Dictionary<string, AssetBundle>();

    /// <summary>
    /// 资源服务器地址
    /// </summary>
    string serverRoot = "https://www.ganghood.net.cn/Game/";

    /// <summary>
    /// assetbundle文件的更新地址
    /// </summary>
    string abRoot;
    /// <summary>
    /// AssetBundleManifest
    /// </summary>
    public AssetBundleManifest abm;
    /// <summary>
    /// 没有依赖文件的AB
    /// </summary>
    Dictionary<string, ABItem> lABs = new Dictionary<string, ABItem>();
    /// <summary>
    /// 有依赖文件的AB
    /// </summary>
    Dictionary<string, ABItem> dABs = new Dictionary<string, ABItem>();

    /// <summary>
    /// 大厅ab加载完毕
    /// </summary>
    private Action OnLobbyLoaded;
    /// <summary>
    /// 游戏ab加载完成
    /// </summary>
    private Action OnGameLoaded;

    public void Init(Action action)
    {
        Instance = this;
        if (action != null)
        {
            OnLobbyLoaded += action;
        }
        abRoot = serverRoot + Helper.GetPlatformName(Application.platform) + "/";
        StartCoroutine(LaodABM());
    }

    /// <summary>
    /// 加载AssetBundleManifest文件
    /// </summary>
    /// <param name="action"></param>
    /// <returns></returns>
    IEnumerator LaodABM()
    {
        string platform = PathManager.Instance.GetPlatformName(Application.platform);
        string abmPath = "";
        if (PathManager.Instance.isEditor())
        {
            abmPath = PathManager.Instance.abStreamingAssetsPathRoot + platform + ".t";
        }
        else
        {
            abmPath = PathManager.Instance.abPersistentDataPathRoot + platform + ".t";
        }
        Log.Debug("abmPath:" + abmPath);
        using (UnityWebRequest uwr = UnityWebRequestAssetBundle.GetAssetBundle(abmPath))
        {
            yield return uwr.SendWebRequest();

            if (uwr.isNetworkError || uwr.isHttpError)
            {
                Log.Error(string.Format("加载AssetBundleManifest失败:{0}", abmPath));
            }
            else
            {
                // Get downloaded asset bundle
                AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(uwr);
                abm = bundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
                if (abm != null)
                {
                    Log.Warning("abm加载完成");
                    LoadLobbyAB();
                }
            }
        }
    }

    #region 测试整体的预加载耗时
    Stopwatch load = new Stopwatch();
    #endregion

    #region 加载大厅资源
    /// <summary>
    /// 加载大厅AB文件
    /// </summary>
    private void LoadLobbyAB()
    {
        #region 测试整体的预加载耗时
        load.Start();
        #endregion
        string[] abNames = abm.GetAllAssetBundles();
        Log.Warning("AB总数:" + abNames.Length);
        foreach (string abName in abNames)
        {
            //if (!abName.StartsWith("game"))
            {
                string[] dNames = abm.GetAllDependencies(abName);
                if (dNames != null && dNames.Length > 0)
                {
                    ABItem ab = new ABItem();
                    ab.name = abName;
                    foreach (string dName in dNames)
                    {
                        ABItem dAB = new ABItem();
                        dAB.name = dName;
                        ab.dABs.Add(dAB);
                    }
                    if (!dABs.ContainsKey(abName))
                    {
                        dABs.Add(abName, ab);
                    }
                }
                else
                {
                    ABItem ab = new ABItem();
                    ab.name = abName;
                    if (!lABs.ContainsKey(abName))
                    {
                        lABs.Add(abName, ab);
                    }
                }
            }
        }
        Log.Warning("大厅无依赖:" + lABs.Count);
        Log.Warning("大厅有依赖:" + dABs.Count);
        abCount = lABs.Count;
        //先把没有依赖的ab加载完
        foreach (ABItem ab in lABs.Values)
        {
            StartCoroutine(LoadLAB(ab, LLoaded));
        }
    }

    private void LLoaded()
    {
        Log.Warning("大厅无依赖的AB加载完毕");
        abCount = dABs.Count;
        //再加载大厅有依赖的ab
        foreach (ABItem ab in dABs.Values)
        {
            StartCoroutine(LoadLAB(ab, LobbyLoaded));
        }
    }

    /// <summary>
    /// 大厅ab加载完成
    /// </summary>
    private void LobbyLoaded()
    {
        Log.Warning("大厅有依赖的AB加载完毕");
        #region 测试预加载整体耗时
        load.Stop();
        Log.Warning("大厅加载耗时：" + load.ElapsedMilliseconds);
        load.Reset();
        #endregion  
        if (OnLobbyLoaded != null)
        {
            OnLobbyLoaded();
            OnLobbyLoaded = null;
        }
    }
    #endregion

    #region 加载游戏的资源
    /// <summary>
    /// 游戏没有依赖文件的AB
    /// </summary>
    Dictionary<string, ABItem> mlABs = new Dictionary<string, ABItem>();
    /// <summary>
    /// 游戏有依赖文件的AB
    /// </summary>
    Dictionary<string, ABItem> mdABs = new Dictionary<string, ABItem>();
    /// <summary>
    /// 加载游戏AB文件
    /// </summary>
    public void LoadGameAB(string gameName, Action action)
    {
        #region 测试整体的预加载耗时
        load.Start();
        #endregion
        if (action != null)
        {
            OnGameLoaded += action;
        }

        string[] abNames = abm.GetAllAssetBundles();
        Log.Warning("AB总数:" + abNames.Length);
        foreach (string abName in abNames)
        {
            if (abName.StartsWith("game"))
            {
                string[] dNames = abm.GetAllDependencies(abName);
                if (dNames != null && dNames.Length > 0)
                {
                    ABItem ab = new ABItem();
                    ab.name = abName;
                    foreach (string dName in dNames)
                    {
                        ABItem dAB = new ABItem();
                        dAB.name = dName;
                        ab.dABs.Add(dAB);
                    }
                    if (!mdABs.ContainsKey(abName))
                    {
                        mdABs.Add(abName, ab);
                    }
                }
                else
                {
                    ABItem ab = new ABItem();
                    ab.name = abName;
                    if (!mlABs.ContainsKey(abName))
                    {
                        mlABs.Add(abName, ab);
                    }
                }
            }
        }
        Log.Warning("单个游戏无依赖:" + mlABs.Count);
        Log.Warning("单个游戏有依赖:" + mdABs.Count);
        abCount = mlABs.Count;
        foreach (ABItem ab in mlABs.Values)
        {
            StartCoroutine(LoadLAB(ab, SingleLoaded));
        }
    }

    private void SingleLoaded()
    {
        Log.Warning("游戏无依赖的AB加载完毕");
        abCount = mdABs.Count;
        foreach (ABItem ab in mdABs.Values)
        {
            StartCoroutine(LoadLAB(ab, GameLoaded));
        }
    }

    /// <summary>
    /// 游戏ab加载完成
    /// </summary>
    private void GameLoaded()
    {
        #region 测试预加载整体耗时
        load.Stop();
        Log.Warning("游戏加载耗时：" + load.ElapsedMilliseconds);
        load.Reset();
        #endregion 
        Log.Warning("单个游戏有依赖的AB加载完毕");
        foreach (var kv in mlABs)
        {
            lABs.Add(kv.Key, kv.Value);
        }
        foreach (var kv in mdABs)
        {
            dABs.Add(kv.Key, kv.Value);
        }
        if (OnGameLoaded != null)
        {
            OnGameLoaded();
            OnGameLoaded = null;
        }
    }

    public void UnLoadGameAB(string gameName)
    {
        List<string> labss = mlABs.Keys.ToList();
        List<ABItem> labsv = mlABs.Values.ToList();
        for (int i = 0; i < labsv.Count; i++)
        {
            if (labss[i].StartsWith(gameName))
            {
                if (labsv[i].ab != null)
                {
                    labsv[i].ab.Unload(true);
                    labsv[i].ab = null;
                }

                mlABs.Remove(labss[i]);
                lABs.Remove(labss[i]);
            }
        }

        List<string> dabss = mdABs.Keys.ToList();
        List<ABItem> dabsv = mdABs.Values.ToList();
        for (int i = 0; i < dabsv.Count; i++)
        {
            if (dabss[i].StartsWith(gameName))
            {
                if (dabsv[i].ab != null)
                {
                    dabsv[i].ab.Unload(true);
                    dabsv[i].ab = null;
                }
                mdABs.Remove(dabss[i]);
                dABs.Remove(dabss[i]);
            }
        }
        Resources.UnloadUnusedAssets();
    }
    #endregion

    /// <summary>
    /// 需要加载的ab数
    /// </summary>
    private int abCount = 0;
    /// <summary>
    /// 已加载的ab数
    /// </summary>
    private int loadedCount = 0;
    /// <summary>
    /// 加载AB
    /// </summary>
    /// <param name="abName"></param>
    /// <param name="action"></param>
    /// <returns></returns>
    private IEnumerator LoadLAB(ABItem ab, Action action)
    {
        Log.Debug("ab.name:" + ab.name);
        AssetBundleCreateRequest abcr = AssetBundle.LoadFromFileAsync(Application.streamingAssetsPath + "/" + ab.name + ".t");
        yield return abcr;
        if (abcr.assetBundle != null)
        {
            ab.ab = abcr.assetBundle;
            loadedCount += 1;
        }
        if (loadedCount == abCount)
        {
            abCount = 0;
            loadedCount = 0;
            if (action != null)
            {
                action();
            }
        }
    }

    public void LoadText(string abName, Action<string> action)
    {
        //ABItem item = GetAB(abName);
        //if (item == null)
        //{
        //    return;
        //}
        //string text = null;
        //UnityEngine.Object obj = item.ab.LoadAsset(objName, type);
        //if (item.ab.mainAsset != null)
        //{
        //    text = item.ab.mainAsset.ToString();
        //}
        //else
        //{
        //    UnityEngine.Object[] assets = item.ab.LoadAllAssets();
        //    if (assets != null)
        //    {
        //        if (assets.Length > 0)
        //        {
        //            text = assets[0].ToString();
        //        }
        //    }
        //}
        //action(text);
    }

    /// <summary>
    /// 加载声音资源，由AudioManager自己管理缓存
    /// </summary>
    /// <param name="abName">相对于Resources路径下的资源路径</param>
    /// <param name="action">回调函数的参数为实例化后的AudioClip对象</param>
    public void LoadAudio(string abName, Action<AudioClip> action)
    {
        string[] temp = abName.Split('.');
        if (temp.Length > 0)
        {
            string clipName = temp[temp.Length - 1];
            action(Load(abName, clipName, typeof(AudioClip)) as AudioClip);
        }
    }

    /// <summary>
    /// 加载指定图集上的sprite
    /// </summary>
    /// <param name="abName"></param>
    /// <param name="spriteName"></param>
    /// <returns></returns>
    public Sprite LoadSprite(string abName, string spriteName)
    {
        return Load(abName, spriteName, typeof(Sprite)) as Sprite;
    }

    /// <summary>
    /// 加载ab中的资源
    /// </summary>
    /// <param name="abName"></param>
    /// <param name="objName"></param>
    /// <param name="type"></param>
    /// <returns></returns>
    private UnityEngine.Object Load(string abName, string objName, Type type)
    {
        ABItem item = GetAB(abName);
        if (item == null)
        {
            return null;
        }
        UnityEngine.Object obj = item.ab.LoadAsset(objName, type);

        if (obj != null)
        {
            return obj;
        }
        return null;
    }

    /// <summary>
    /// 加载ab中的资源
    /// </summary>
    /// <param name="abName"></param>
    /// <param name="objName"></param>
    /// <param name="type"></param>
    /// <returns></returns>
    public void Load(string abName, string objName, Action<GameObject> action)
    {
        StartCoroutine(Load(abName, action));
    }

    private ABItem GetAB(string abName)
    {
        ABItem ab = null;
        dABs.TryGetValue(abName, out ab);
        if (ab == null)
        {
            lABs.TryGetValue(abName, out ab);
            if (ab == null)
            {
                Log.Debug(abName + " ab文件还未未加载");
                return null;
            }
        }
        return ab;
    }

    public Texture2D LoadTexture(string abName, string textureName)
    {
        return Load(abName, textureName, typeof(Texture2D)) as Texture2D;
    }

    public Font LoadFont(string abName, string fontName)
    {
        return Load(abName, fontName, typeof(Font)) as Font;
    }

    /// <summary>
    /// 加载UI的prefab并实例化 
    /// </summary>
    /// <param name="abName"></param>
    /// <param name="action"></param> 
    public void LoadUI(string abName, Action<GameObject> action)
    {
        StartCoroutine(Load(abName, action));
    }

    /// <summary>
    /// 加载
    /// </summary>
    /// <param name="name"></param>
    /// <returns></returns>
    private IEnumerator Load(string abName, Action<GameObject> action)
    {
        ABItem ab = null;
        dABs.TryGetValue(abName, out ab);
        if (ab == null)
        {
            lABs.TryGetValue(abName, out ab);
            if (ab == null)
            {
                Log.Debug(abName + " ab文件还未未加载");
                yield break;
            }
        }
        AssetBundleRequest abr = ab.ab.LoadAllAssetsAsync();
        yield return abr;

        if (abr.allAssets != null)
        {
            if (abr.allAssets.Length > 0)
            {
                GameObject go = Instantiate(abr.allAssets[0]) as GameObject;
                action(go);
            }
        }
    }

    public void ClearAll(bool state)
    {
        foreach (var kv in lABs)
        {
            kv.Value.ab.Unload(true);
            kv.Value.ab = null;
        }
        lABs.Clear();
        foreach (var kv in dABs)
        {
            kv.Value.ab.Unload(true);
            kv.Value.ab = null;
        }
        dABs.Clear();
        Resources.UnloadUnusedAssets();
        GC.Collect();
    }

    private void OnApplicationQuit()
    {
        ClearAll(true);
    }
}